/* vim: set sw=8 ts=8 sts=8 expandtab: */
#ifndef CMAID_ITER_FUNCDEF_H
#define CMAID_ITER_FUNCDEF_H
#include <stdlib.h>

/**
 * @def CMAID_ITER_FUNC(it)
 * @brief Macro to get the iterator interface
 * @ingroup Cmaid_Iter_Interface
 */
#define CMAID_ITER_FUNC(it) ((it)->interface)

/**
 * @return Returns a newly allocated iterator
 * @brief Allocates an iterator
 *
 * This function will return the newly allocated memory for a Cmaid_Iter.
 * @note It is recommed to use the stack memory for the iterator or if the
 *       iterator is always present to place the iterator directly into the
 *       surrounding structure. If both ways are not possible use this
 *       function.
 */
CMAID_INLINE Cmaid_Iter *
cmaid_iter_new(void)
{
        /* there are debates about casting the result of malloc
         * but this header might also be used in C++ apps, hence simply
         * cast it */
        return (Cmaid_Iter *)calloc(1, sizeof(Cmaid_Iter));
}

/**
 * @param it The iterator to free
 * @return Returns no value
 * @brief Frees an iterator
 *
 * This function will free the give iterator @p it. Use this function only
 * if you have allocated the iterator with cmaid_iter_new() before.
 *
 * @note This function will not detach the iterator from its parent container!
 */
CMAID_INLINE void
cmaid_iter_destroy(Cmaid_Iter *it)
{
        free(it);
}

/**
 * @param it the iterator to move
 * @return Returns no value
 * @brief Move the iterator to the start
 *
 * Move the iterator to the start. The start is a virtual item quite before
 * the first node and quite after the last node. This is normal the place where
 * you start to iterate
 */
CMAID_INLINE void
cmaid_iter_start(Cmaid_Iter *it)
{
        CMAID_ITER_FUNC(it)->start(it);
}

/**
 * @param it the iter to move
 * @param to_it the new position for the iterator
 * @brief move the given iterator to the second iterator's place
 *
 * This function moves the given iterator to the same position like
 * the given iterator has. This will fail if the iterators are for
 * different containers.
 */
CMAID_INLINE void *
cmaid_iter_iter_goto(Cmaid_Iter *it, const Cmaid_Iter *to_it)
{
        return CMAID_ITER_FUNC(it)->iter_goto(it, to_it);
}

/**
 * @param it the iterator to move
 * @param index the new position of the iterator
 * @return Return the data pointer of the node at the index position
 * @brief move the iterator to the give position
 *
 * Move the iterator to the give index position, if the index is smaller 0 or
 * the index is greater than the actual container count or if the container is
 * empty, this call will perform the same as cmaid_iter_start() and thus
 * return NULL.
 */
CMAID_INLINE void *
cmaid_iter_index_goto(Cmaid_Iter *it, int index)
{
        return CMAID_ITER_FUNC(it)->index_goto(it, index);
}

/**
 * @param it the iterator to move
 * @param data the data pointer to find
 * @return Return the @p data pointer on success else @c NULL
 * @brief move the iterator to the given @p data pointer
 *
 * Move the iterator to the give data pointer and return @p data, if the data
 * pointer is not found the iterator will point to the empty element and
 * thus the return value will be @c NULL.
 * 
 * @note This function compares the plain pointers and does not regard the
 *       actual data representation, i.e. it will not use any compare function.
 */
CMAID_INLINE void *
cmaid_iter_goto(Cmaid_Iter *it, const void *data)
{
        return CMAID_ITER_FUNC(it)->data_goto(it, data);
}

/**
 * @param it the iterator to get the index
 * @return the index of the node the iterator is pointing to
 * @brief Retrieve the current index
 *
 * This function will return the current pointer position.
 */
CMAID_INLINE int
cmaid_iter_index_get(Cmaid_Iter *it)
{
        return it->index;
}

/**
 * @param it The iterator to work with
 * @return Return the parent container of the given iterator
 * @brief Retrieve the parent container of the iterator
 *
 * This function returns the parent container of the given iterator
 */
CMAID_INLINE void *
cmaid_iter_container_get(Cmaid_Iter *it)
{
        return it->container;
}

/**
 * @param it The iterator to retrieve the current data
 * @return Returns a pointer to the current data pointer
 * @brief Returns the data pointer of the current node
 *
 * This functions returns the data pointer of the current node. If the
 * iterator does not point to a valid data node, i.e. the iterator is not
 * attached to a container or the iterator is pointing to the start point,
 * it will return @c NULL.
 */
CMAID_INLINE void *
cmaid_iter_current(Cmaid_Iter *it)
{
        return CMAID_ITER_FUNC(it)->current(it);
}

/**
 * @param it The iterator to retrieve the next data
 * @return Returns a pointer to the next data pointer
 * @brief Walks to the next node and returns its data pointer
 *
 * This function call will move the iterator to the next item inside of the
 * container and return the data pointer of it. If the iterator is pointing
 * to the virtual start node it will go to the first node in the container.
 * If it is pointing to the last node of the container it will go back to the
 * virtual start node and returns @c NULL.
 */
CMAID_INLINE void *
cmaid_iter_next(Cmaid_Iter *it)
{
        return CMAID_ITER_FUNC(it)->next(it);
}

/**
 * @param it The iterator to retrieve the previous data
 * @return Returns a pointer to the previous data pointer
 * @brief Walks to the previous node and returns its data pointer
 *
 * This function call will move the iterator to the previous item inside of the
 * container and return the data pointer of it. If the iterator is pointing
 * to the virtual start node it will go to the last node in the container.
 * If it is pointing to the first node of the container it will go back to the
 * virtual start node and returns @c NULL.
 */
CMAID_INLINE void *
cmaid_iter_previous(Cmaid_Iter *it)
{
        return CMAID_ITER_FUNC(it)->previous(it);
}

/**
 * @param it The iterator to removoe the current node
 * @return Returns no value
 * @brief Remove the current node
 *
 * This function will remove the current node and perform a step backwards. So
 * if you are calling next you will land on the same node as if you hadn't
 * remove the selected node.
 */
CMAID_INLINE void
cmaid_iter_remove(Cmaid_Iter *it)
{
        CMAID_ITER_FUNC(it)->remove(it);
}

#endif /* CMAID_ITER_FUNCDEF_H */
